﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using ForumServer;
using ForumServer.Forum;

using log4net;
using System.Net.Sockets;



namespace ForumClient
{
    class ClientHandler
    {
        private bool returnFlag = false;
        private ILog logger;

        public ClientHandler(ref ILog ilogger)
        {
            logger = ilogger;
        }
        /// <summary>
        /// Adds a new post to thread t
        /// and sets it's creator to be user
        /// </summary>
        /// <param name="t">the thread to add the post to</param>
        /// <param name="user">the creator of the post</param>
        public void handleAddNewPost(Thread t, User user)
        {
            Console.Write("Please enter the post header:");
            String header = Console.ReadLine();
            Console.Write("Please enter the post content:");
            String content = Console.ReadLine();
            Post p = new Post(header, content, DateTime.Now, user);
            t.addNewPost(p);
            t.LastDate = p.TimeStamp;
            t.NumOfReplies++;
            Console.WriteLine("Post added succesfully");
        }
        
        /// <summary>
        /// Edit an existing post in thread t
        /// the user has to be the creator of the post
        /// </summary>
        /// <param name="t">the thread where the post exists in</param>
        /// <param name="user">the creator of the post</param>
        public void handleEditPost(Thread t, User user)
        {
            if (t.PostList.Count == 0)
            {
                Console.WriteLine("ERROR: there is no post at this thread!!!\n");
                return;
            }
            do
            {
                Console.WriteLine("Choose which post you would like to edit (-1 to cancel):");
                String num = Console.ReadLine();
                if (String.IsNullOrEmpty(num))
                    continue;
                
                int index = int.Parse(num);
                if (index == -1)
                    break;
                if (index > 0 && index <= t.PostList.Count)
                {
                    if (t.PostList[index - 1].User.Equals(user))
                    {
                        Console.Write("Enter new header:");
                        String header = Console.ReadLine();
                        Console.Write("Enter new content:");
                        String content = Console.ReadLine();
                        Post p = new Post(header, content, DateTime.Now, user);
                        if (t.editPostAt(index - 1, p))
                        {
                            Console.WriteLine("Post changed succesfully\n");
                            t.LastDate = p.TimeStamp;
                        }
                        else
                            Console.WriteLine("ERROR: Problem occur while edit post, please try again\n\n");
                        
                        break;
                    }
                    else
                        Console.WriteLine("ERROR: Cannot edit post that you didn't create!!!\n\n");
                }
                else 
                    Console.WriteLine("ERROR: invalid post index!!!");
            } while (true);
           
        }
        
        /// <summary>
        /// Removes a post from the thread t
        /// only the creator of the post can remove it
        /// </summary>
        /// <param name="t">the thread where the post exists in</param>
        /// <param name="user">the creator of the post</param>
        public void handleRemovePost(Thread t, User user)
        {
            if (t.PostList.Count == 0)
            {
                Console.WriteLine("ERROR: there is no post at this thread!!!\n");
                return;
            }
            do
            {
                Console.WriteLine("Choose which post you would like to remove (-1 to cancel):");
                String num = Console.ReadLine();
                if (String.IsNullOrEmpty(num))
                    continue;
                int index = int.Parse(num);
                if (index == -1)
                    break;
                if (index > 0 && index <= t.PostList.Count)
                {
                    if (t.PostList[index - 1].User.Equals(user))
                    {
                        if(t.removePostAt(index - 1))
                            Console.WriteLine("Post removed succesfully");
                        else
                            Console.WriteLine("Post wasn't removed succesfully");
                        break;
                    }
                    else
                        Console.WriteLine("ERROR: Cannot remove post that you didn't create!!!\n\n");
                }
                else
                    Console.WriteLine("ERROR: invalid post index!!!");
            } while (true);
        }
        
        /// <summary>
        /// View a thread and choose whether to 
        /// add a post, edit a post or to remove a post
        /// </summary>
        /// <param name="forum">the forum in which the thread exists in</param>
        /// <param name="thread">the thread that is viewed</param>
        /// <param name="user">the user</param>
        public void handleViewPostCase(Forum forum, Thread thread, User user)
        {
            int c = 0;
            thread.NumOfViews++;
            do
            {
                Console.WriteLine("\n" + thread.ToString());

                Console.WriteLine("\nWhat would you like to do?");
                Console.WriteLine("1.   Add new post");
                Console.WriteLine("2.   Edit post");
                Console.WriteLine("3.   Remove post");
                Console.WriteLine("4.   Return");
                
           
                String choose = Console.ReadLine();
                if (String.IsNullOrEmpty(choose))
                    continue;
                c = int.Parse(choose);
                switch (c)
                {
                    case 1:
                        handleAddNewPost(thread, user);
                        break;
                    case 2:
                        handleEditPost(thread, user);
                        break;
                    case 3:
                        handleRemovePost(thread, user);
                        break;
                    case 4:
                        break;
                    default:
                        Console.WriteLine("ERROR: Invalid choice");
                        Console.WriteLine("Please choose number between 1 and 4");
                        break;
                }
            } while (c != 4);
        }

        /// <summary>
        /// View thread headers and decide which thread
        /// to view and/or edit
        /// </summary>
        /// <param name="forum">the forum in which the threads exist in</param>
        /// <param name="user">the user</param>
        public void handleViewThreadCase(Forum forum, User user)
        {
            Console.WriteLine("Threads");
            for (int i = 0; i < forum.ThreadList.Count; i++)
            {
                Console.WriteLine((i + 1).ToString() + ".   " + forum.ThreadList[i].Header);
            }
            do
            {
                Console.WriteLine("choose thread you would like to view (-1 to cancel).");
                String option1 = Console.ReadLine();
                if (String.IsNullOrEmpty(option1))
                    continue;
                int index = int.Parse(option1);
                if (index == -1)
                    break;
                else if (index > 0 && index <= forum.ThreadList.Count)
                {
                    handleViewPostCase(forum, forum.ThreadList[index-1], user);
                    break;
                }
                Console.WriteLine("ERROR: invalid choice!!!\n");
               
            } while (true);
        }

        /// <summary>
        /// Choose to view friends list
        /// add a new friend
        /// or to remove an existing friend
        /// </summary>
        /// <param name="forum">the forum the user is in</param>
        /// <param name="user">the user</param>
        /// <param name="auth">the authenticator that holds the users</param>
        public void handleEditFriendsList(Forum forum, User user, Authenticator auth)
        {
            this.returnFlag = false;
            while (!returnFlag)
            {
                Console.WriteLine("");
                Console.WriteLine("What would you like to do?");
                Console.WriteLine("1.   View My Friends List");
                Console.WriteLine("2.   Add New Friend");
                Console.WriteLine("3.   Remove Friend");
                Console.WriteLine("4.   Return");
                String option = Console.ReadLine();

                if (String.IsNullOrEmpty(option)){
                    Console.WriteLine("Error: (not a leagal choice)");
                    break;
                }

                switch(option)
                {
                    case "1":
                        try
                        {
                            this.handleViewFriends(forum, user);    
                            logger.Info("Viewed friends");
                        }
                            catch(Exception ex)
                        {
                                Console.WriteLine("Failed to view friends");
                            logger.Error("Failed to view friends");
                            if (ex is SocketException)
                            {
                                throw new SocketException();
                            }
                        }
                        break;
                    case "2":
                        try
                        {
                            this.handleAddNewFriend(forum, user, auth);    
                            logger.Info("Added new friends");
                        }
                        catch(Exception ex)
                        {
                            Console.WriteLine("Failed to add new friends");
                            logger.Error("Failed to add new friends");
                            if (ex is SocketException)
                            {
                                throw new SocketException();

                            }
                        }
                        break;
                    case "3":
                        try
                        {
                            this.handleRemoveFriend(forum, user, auth);
                            logger.Info("Removed friend");
                        }
                        catch(Exception ex)
                        {
                            Console.WriteLine("Failed to Remove Friend");
                            logger.Error("Failed to Remove Friend");
                            if (ex is SocketException)
                            {
                                throw new SocketException();
                            }
                        }
                        break;
                    case "4":
                        this.handleReturn();
                        break;
                    default:
                        Console.WriteLine("Please choose a number and hit Enter");
                        Console.WriteLine("");
                        break;
                }
                    
            }
        }

        /// <summary>
        /// return to the last menu
        /// </summary>
        private void handleReturn()
        {
            Console.WriteLine("Thank you!\n");
            this.returnFlag = true;
            return;
        }

        /// <summary>
        /// View the friends list
        /// </summary>
        /// <param name="forum">the forum the user is in</param>
        /// <param name="user">the user</param>
        public void handleViewFriends(Forum forum, User user)
        {
            Console.WriteLine(user.getFriendslist());
            Console.WriteLine("");
        }

        /// <summary>
        /// Add a new friend by search or
        /// by choosing from a list
        /// </summary>
        /// <param name="forum">the forum the user is in</param>
        /// <param name="user">the user</param>
        /// <param name="auth">the authenticator that holds the users</param>
        public void handleAddNewFriend(Forum forum, User user, Authenticator auth)
        {
            Console.WriteLine("");
            Console.WriteLine("What would you like to do?");
            Console.WriteLine("1.   Choose Friend From Users List");
            Console.WriteLine("2.   Search For New Friend");
            Console.WriteLine("3.   Return");
            String newFriendOption = Console.ReadLine();
            if (String.IsNullOrEmpty(newFriendOption)){
                Console.WriteLine("Error: (not a leagal option)");
                return;
            }
            switch (newFriendOption)
            {
                case "1":
                    Console.WriteLine("Current Forum users are");
                    handleChooseFriendFromList(forum, user, auth, auth.UsersList);
                    break;
                case "2":
                    handleSearchForNewFriend(forum, user, auth);
                    break;
                default:
                    Console.WriteLine("Please choose a number and hit Enter\n");
                    break;
            }
        }

        /// <summary>
        /// Search for a friend from the existing registered users
        /// </summary>
        /// <param name="forum">the forum the user is in</param>
        /// <param name="user">the user</param>
        /// <param name="auth">the authenticator that holds the users</param>
        private void handleSearchForNewFriend(Forum forum, User user, Authenticator auth)
        {
            Console.WriteLine("Enter username to search for:\n");
            String tUserName = Console.ReadLine();
            List<User> tUsers = new List<User>();
            for (int i = 0; i < auth.UsersList.Count; i++)
            {
                if (auth.UsersList[i].Username.Contains(tUserName))
                    tUsers.Add(auth.UsersList[i]);
            }
            if(tUsers.Count>0)
                handleChooseFriendFromList(forum, user, auth, tUsers);
            else{
                Console.WriteLine("Your search - "+tUserName+" - did not match any users.\n");
                this.handleEditFriendsList(forum, user, auth);
            }
        }

        /// <summary>
        /// Add a friend by choosing from the existing registered list
        /// </summary>
        /// <param name="forum">the forum the user is in</param>
        /// <param name="user">the user</param>
        /// <param name="auth">the authenticator that holds the users</param>
        /// <param name="usersList">the existing registered users</param>
        private void handleChooseFriendFromList(Forum forum, User user, Authenticator auth, List<User> usersList)
        {
            for (int i = 0; i < usersList.Count; i++)//TO DO:could be a problem with this check
            {
                if (usersList[i] != user)
                    Console.WriteLine((i + 1).ToString() + ".   " + usersList[i].ToString());
            }
            Console.WriteLine("Enter the numbers of friends you want to add, seperated with a comma (\",\")");
            string selectedIndexs = Console.ReadLine();
            string[] IndexOfFriends = selectedIndexs.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);//check index in range!!
            foreach (string StringIndex in IndexOfFriends)
            {
                int index = int.Parse(StringIndex) - 1;
                if (index >= 0 & index < usersList.Count)
                {
                    if (user.Friends.Contains(usersList[index]))
                        Console.WriteLine(usersList[index] + " already in friends list!");
                    else if (user.Equals(usersList[index]))
                        Console.WriteLine("You can't be friend of yourself!");
                    else
                    {
                        user.addFriend(usersList[index]);
                        Console.WriteLine(usersList[index] + " has been added to your friends list!");
                        logger.Info(usersList[index] + " has been added to client friends list");
                    }
                }
                else
                    Console.WriteLine("You have selected a wrong index for friend!");
            }
            Console.WriteLine("");
        }

        /// <summary>
        /// Remove friend from friends list
        /// </summary>
        /// <param name="forum">the forum the user is in</param>
        /// <param name="user">the user</param>
        /// <param name="auth">the authenticator that holds the users</param>
        internal void handleRemoveFriend(Forum forum, User user, Authenticator auth)
        {
            Console.WriteLine("Current User Friends are");
            for (int i = 0; i < user.Friends.Count; i++)
            {
                Console.WriteLine((i + 1).ToString() + ".   " + user.Friends[i].ToString());
            }
            Console.WriteLine("Enter friend index you want to remove");
            string selectedIndex = Console.ReadLine();
            int index = -1;
            try
            {
                index = int.Parse(selectedIndex) - 1;
                User tSelectedFriend = user.Friends.ElementAt(index);
                user.removeFriend(tSelectedFriend);
                Console.WriteLine(tSelectedFriend+" has been removed from the list!\n");
            }
            catch
            {
                Console.WriteLine("Failed to remove a friend");
                logger.Error("Failed to remove a friend");
            }
            Console.WriteLine("");
        }
        
        /// <summary>
        /// Remove an existing thread
        /// only the creator of the thread can remove it
        /// (needs to change later)
        /// </summary>
        /// <param name="forum">the forum the thread exists in</param>
        /// <param name="user">the creator of the thread</param>
        internal void handleRemoveThread(Forum forum, User user)
        {
            Console.WriteLine("Showing all threads:\n");
            for (int i = 0; i < forum.ThreadList.Count; i++)
            {
                Console.WriteLine((i + 1).ToString() + ".   " + forum.ThreadList[i].Header);
            }
            do
            {
                Console.Write("Which thread would you like to remove? (-1 to cancel): ");
                String rm = Console.ReadLine();//read selection or esc to cancel
                Console.WriteLine("");
                int threadNum = int.Parse(rm);
                if (threadNum == -1)
                {
                    Console.WriteLine("");
                    break;
                }
                if (threadNum > 0 && threadNum <= forum.ThreadList.Count)
                {
                    if (forum.ThreadList.ElementAt(threadNum-1).Creator.Equals(user))
                    {
                        // delete the thread
                        forum.removeAt(threadNum-1);
                        Console.WriteLine("Thread number " + threadNum + " deleted!!!\n\n");
                        break;
                    }
                    Console.WriteLine("ERROR: Cannot remove thread that you didn't create!!!\n\n");
                }
                else
                    Console.WriteLine("ERROR: thread index doesn't exist!!!\n\n");
            } while (true);
        }
        
        /// <summary>
        /// Add a thread to the forum
        /// </summary>
        /// <param name="forum">the forum to add the thread to</param>
        /// <param name="user">the creator of the thread</param>
        internal void handleAddThread(Forum forum, User user)
        {
            Console.Write("Enter a Topic: ");
            String header = Console.ReadLine();
            Console.Write("Enter the content of the thread: ");
            String content = Console.ReadLine();

            try
            {
                ForumServer.Forum.Thread thread = new ForumServer.Forum.Thread(header, content, user);
                forum.addThread(thread);
                logger.Info("Added thread with the topic: " + header);
            }
            catch
            {
                logger.Error("Failed to add the thread with the topic: " + header);
            }
            Console.WriteLine("");
        }


        public void handleErrors(Delegate method, object[] param, String error)
        {
            bool cancel = false;
            Console.WriteLine(error);

            while (!cancel)
            {
                Console.WriteLine("");
                Console.WriteLine("What would like to do?");
                Console.WriteLine("1.   Try again");
                Console.WriteLine("2.   Cancel");
                String option = Console.ReadLine();

                switch (option)
                {
                    case "1":
                        method.DynamicInvoke(param);
                        cancel = true;
                        break;
                    case "2":
                        this.handleReturn();
                        cancel = true;
                        break;
                    default:
                        Console.WriteLine("Please choose a number and hit Enter");
                        Console.WriteLine("");
                        break;
                }
            }
        }
        /// <summary>
        /// read from console
        /// if Esc is enterd return "-1"
        /// </summary>
        /// <returns></returns>
        private static String readEsc()
        {
            String input = "";
            ConsoleKeyInfo info = Console.ReadKey(false);
            while (info.Key != ConsoleKey.Enter)
            {
                if (info.Key == ConsoleKey.Escape)
                {
                    input = "-1";
                    break;
                }
                if (info.Key != ConsoleKey.Backspace)
                {
                    input += info.KeyChar;
                    info = Console.ReadKey(false);
                }
                else if (info.Key == ConsoleKey.Backspace)
                {
                    if (!string.IsNullOrEmpty(input))
                    {
                        input = input.Substring
                        (0, input.Length - 1);
                    }
                    info = Console.ReadKey(false);
                }
            }
            return input;
        }

        /// <summary>
        /// this function allows the user to choose a forum.
        /// </summary>
        internal void handleChooseAForum(ref Forum forum, Authenticator auth)
        {
            Console.WriteLine("Choose a Forum from list:");
            Console.WriteLine("");
            for (int i = 0; i < auth.ForumList.Count; i++)
            {
                Console.WriteLine((i + 1).ToString() + ".   " + auth.ForumList[i].ToString());
            }
            string selectedIndex = Console.ReadLine();
            int index = -1;
            try
            {
                index = int.Parse(selectedIndex) - 1;
                ClientConfiguration.configureLoginToForum(ref forum,logger,index);
            }
            catch
            {
                Console.WriteLine("Failed to choose a forum!");
                logger.Error("Failed to choose a forum!");
            }
            Console.WriteLine(""); 
        }
    }
}
